/* HTTP GET Example using plain POSIX sockets

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "my_app.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_http_client.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "sdkconfig.h"
#include <portmacro.h>
#include "esp_mac.h"
#include <sys/param.h>
#include <stdlib.h>
#include <ctype.h>
#include "esp_tls.h"
#include "cJSON.h"

#define MAX_HTTP_RECV_BUFFER 512
#define MAX_HTTP_OUTPUT_BUFFER 2048
static const char *TAG = "my_weather";
weather_data_t weather_data;
// static SemaphoreHandle_t GetWeatherMutex; // 声明一个信号量
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer; // Buffer to store response of http request from event handler
    static int output_len;      // Stores number of bytes read
    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
        // Clean the buffer in case of a new request
        if (output_len == 0 && evt->user_data)
        {
            // we are just starting to copy the output data into the use
            memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
        }
        /*
         *  Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
         *  However, event handler can also be used in case chunked encoding is used.
         */
        if (!esp_http_client_is_chunked_response(evt->client))
        {
            // If user_data buffer is configured, copy the response into the buffer
            int copy_len = 0;
            if (evt->user_data)
            {
                // The last byte in evt->user_data is kept for the NULL character in case of out-of-bound access.
                copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
                if (copy_len)
                {
                    memcpy(evt->user_data + output_len, evt->data, copy_len);
                }
            }
            else
            {
                int content_len = esp_http_client_get_content_length(evt->client);
                if (output_buffer == NULL)
                {
                    // We initialize output_buffer with 0 because it is used by strlen() and similar functions therefore should be null terminated.
                    output_buffer = (char *)calloc(content_len + 1, sizeof(char));
                    output_len = 0;
                    if (output_buffer == NULL)
                    {
                        ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
                        return ESP_FAIL;
                    }
                }
                copy_len = MIN(evt->data_len, (content_len - output_len));
                if (copy_len)
                {
                    memcpy(output_buffer + output_len, evt->data, copy_len);
                }
            }
            output_len += copy_len;
        }

        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
        if (output_buffer != NULL)
        {
            // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
            // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
            free(output_buffer);
            output_buffer = NULL;
        }
        output_len = 0;
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        int mbedtls_err = 0;
        esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
        if (err != 0)
        {
            ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
            ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
        }
        if (output_buffer != NULL)
        {
            free(output_buffer);
            output_buffer = NULL;
        }
        output_len = 0;
        break;
    case HTTP_EVENT_REDIRECT:
        ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
        esp_http_client_set_header(evt->client, "From", "user@example.com");
        esp_http_client_set_header(evt->client, "Accept", "text/html");
        esp_http_client_set_redirection(evt->client);
        break;
    }
    return ESP_OK;
}
void my_weather_task(void *pvParameters)
{
    // Declare local_response_buffer with size (MAX_HTTP_OUTPUT_BUFFER + 1) to prevent out of bound access when
    // it is used by functions like strlen(). The buffer should only be used upto size MAX_HTTP_OUTPUT_BUFFER
    char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER + 1] = {0};
    /**
     * NOTE: All the configuration parameters for http_client must be spefied either in URL or as host and path parameters.
     * If host and path parameters are not set, query parameter will be ignored. In such cases,
     * query parameter should be specified in URL.
     *
     * If URL as well as host and path parameters are specified, values of host and path will be considered.
     */
    // 定义一个esp_http_client_config_t类型的结构体变量config，用于配置HTTP客户端
    esp_http_client_config_t config = {
        .url = "http://api.seniverse.com/v3/weather/daily.json?key=SmGUEiieRk3ICWRqN&location=ip&language=zh-Hans&unit=c&start=0&days=5",

        .event_handler = _http_event_handler, // 设置HTTP事件处理函数
        // 这行代码指定了HTTP事件的处理函数为 _http_event_handler。
        // 当HTTP事件发生时，系统会调用这个函数来处理。
        .user_data = local_response_buffer, // Pass address of local buffer to get response
                                            // .cert_pem = ca_cert_pem_start,      // 设置HTTP客户端的证书
        // 这行代码将本地响应缓冲区的地址传递给用户数据字段。
        // 这样，当HTTP请求得到响应时，可以将响应数据存储到这个缓冲区中。
        .disable_auto_redirect = 1, // 禁用自动重定向
    };
    while (1)
    {
        if (xSemaphoreTake(http_Mutex, portMAX_DELAY) == pdTRUE)
        {

            // 初始化HTTP客户端
            // esp_http_client_handle_t 是一个指向HTTP客户端句柄的指针类型
            // esp_http_client_init 函数用于初始化HTTP客户端，并返回一个客户端句柄
            // &config 是一个指向esp_http_client_config_t类型结构体的指针，该结构体包含了HTTP客户端的配置信息
            esp_http_client_handle_t client = esp_http_client_init(&config);

            // 执行HTTP客户端请求，并获取返回的错误码
            esp_err_t err = esp_http_client_perform(client);
            // 检查返回的错误码是否为ESP_OK，表示请求成功
            if (err == ESP_OK)
            {
                // 如果请求成功，打印HTTP GET请求的状态码和内容长度
                ESP_LOGI(TAG, "HTTP GET 成功Status = %d, content_length = %" PRId64,
                         // 获取HTTP客户端的状态码
                         esp_http_client_get_status_code(client),
                         // 获取HTTP客户端的内容长度
                         esp_http_client_get_content_length(client));
            }
            else
            {
                // 如果请求失败，打印错误信息
                ESP_LOGE(TAG, "HTTP GET request 失败failed: %s", esp_err_to_name(err));
            }
            esp_http_client_cleanup(client); // 清理HTTP客户端

         //   ESP_LOGI(TAG, "HTTP Response: %s", local_response_buffer);

            cJSON *root = cJSON_Parse(local_response_buffer);
            if (root == NULL)
            {
                // 解析失败，处理错误
                // 获取错误指针，指向解析失败的位置
                const char *error_ptr = cJSON_GetErrorPtr();
                // 如果错误指针不为空，打印错误信息
                if (error_ptr != NULL)
                {
                    ESP_LOGE(TAG, "Error before: %s\n", error_ptr);
                }
                // 释放cJSON对象
                cJSON_Delete(root);
                // 返回，结束函数执行
            }
            else
            {

                cJSON *results = cJSON_GetObjectItem(root, "results");
                if (cJSON_IsArray(results))
                {
                    cJSON *result = cJSON_GetArrayItem(results, 0); // Assuming we're interested in the first result
                    if (cJSON_IsObject(result))
                    {
                        cJSON *location = cJSON_GetObjectItem(result, "location");
                        if (cJSON_IsObject(location))
                        {
                            cJSON *name = cJSON_GetObjectItem(location, "name");
                            if (cJSON_IsString(name) && (name->valuestring != NULL))
                            {
                                strcpy(weather_data.city, name->valuestring); // 天气地点名称
                                printf("Location Name: %s\n", name->valuestring);
                            }
                        }

                        cJSON *daily = cJSON_GetObjectItem(result, "daily");
                        if (cJSON_IsArray(daily))
                        {

                            cJSON *day = cJSON_GetArrayItem(daily, 0);
                            if (cJSON_IsObject(day))
                            {
                                cJSON *date = cJSON_GetObjectItem(day, "date");
                                cJSON *text_day = cJSON_GetObjectItem(day, "text_day");
                                strcpy(weather_data.weather, text_day->valuestring); // 天气情况
                                cJSON *code_day = cJSON_GetObjectItem(day, "code_day");
                                weather_data.code = atoi(code_day->valuestring); // 天气情况代码
                                cJSON *high = cJSON_GetObjectItem(day, "high");
                                weather_data.hight_temperature = atoi(high->valuestring); // 最高温度
                                cJSON *low = cJSON_GetObjectItem(day, "low");
                                weather_data.low_temperature = atoi(low->valuestring); // 最低温度
                                cJSON *humidity = cJSON_GetObjectItem(day, "humidity");
                                weather_data.humidity = atoi(humidity->valuestring); // 湿度

                                printf("Date: %s, Day: %s, code : %d, High: %d, Low: %d,humidity : %d\n",
                                       date->valuestring, text_day->valuestring, weather_data.code,
                                       weather_data.hight_temperature, weather_data.low_temperature, weather_data.humidity);
                            }
                        }
                    }
                    if (lv_obj_is_valid(guider_ui.screen_home_spangroup_3))
                    {
                        char buffer[100];
                        sprintf(buffer, "最高温度：%d℃\n\n最低温度：%d℃\n\n湿度：%d%%", weather_data.hight_temperature, weather_data.low_temperature, weather_data.humidity);
                        lv_span_set_text(guider_ui.screen_home_spangroup_3_span, buffer);
                        lv_label_set_text(guider_ui.screen_home_label_weathe, weather_data.weather);
                    }
                }

                cJSON_Delete(root);
            }
 
            xSemaphoreGive(http_Mutex);
        }
       vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}
void get_my_weather(void)
{
    xTaskCreatePinnedToCore(my_weather_task, "my_weather_task", 4096 * 2, NULL, 10, NULL, 0);
}